package com.merlin.security.core.validate.code;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import com.merlin.security.core.properties.SecurityProperties;

/**
 * 图形验证码过滤器
 * 继承OncePerRequestFilter，实现将过滤器加入到SpringSecurity 过滤器链上
 * 实现InitializingBean接口，目的是为了当其他参数都组装完毕之后，再去初始化urls值
 * @author merlin
 *
 */
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{

	
	/**
	 * 验证码校验失败处理器
	 */
	private AuthenticationFailureHandler authenticationFailureHandler;
	
	/**
	 * session工具类
	 */
	private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
	
	/**
	 * 存放需要拦截的url
	 */
	private Set<String> urls = new HashSet<>();
	
	private SecurityProperties securityProperties;
	
	/**
	 * spring 工具类
	 */
	private AntPathMatcher pathMatcher = new AntPathMatcher();
	 
	//重写InitializingBean父类方法
	@Override
	public void afterPropertiesSet() throws ServletException {
		super.afterPropertiesSet();
		/**
		 * 初始化url
		 */
		String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getCode().getImage().getUrl() , ",");
		for (String configUrl : configUrls) {
			//添加的是配置的url
			urls.add(configUrl);
		}
		//添加登陆的请求
		urls.add("/authentication/form");
	}
	
	
	public Set<String> getUrls() {
		return urls;
	}

	public void setUrls(Set<String> urls) {
		this.urls = urls;
	}

	public SecurityProperties getSecurityProperties() {
		return securityProperties;
	}

	public void setSecurityProperties(SecurityProperties securityProperties) {
		this.securityProperties = securityProperties;
	}

	public AuthenticationFailureHandler getAuthenticationFailureHandler() {
		return authenticationFailureHandler;
	}

	public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
		this.authenticationFailureHandler = authenticationFailureHandler;
	}

	public SessionStrategy getSessionStrategy() {
		return sessionStrategy;
	}

	public void setSessionStrategy(SessionStrategy sessionStrategy) {
		this.sessionStrategy = sessionStrategy;
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		
		boolean action =false;
		
		for (String url : urls) {
			if(pathMatcher.match(url, request.getRequestURI())) { //如果配置的url和请求的url匹配的话，就做过滤
				action = true;
			}
		}
		
		
		//如果请求是登陆请求，并且是Post请求，则拦截，否则不做处理
		/*if(StringUtils.equals("/authentication/form", request.getRequestURI())
				&& StringUtils.equalsIgnoreCase(request.getMethod(), "post"))*/ 
		if(action){
			try {
				//校验，从session中取验证码
				validate(new ServletWebRequest(request));
			} catch (ValidateCodeException e) {
				//失败处理
				authenticationFailureHandler.onAuthenticationFailure(request, response, e);
				return;
			}
		}
		//验证通过，调之后的过滤器
		filterChain.doFilter(request, response);

	}

	/**
	 * 验证码校验
	 * @param servletWebRequest
	 * @throws ServletRequestBindingException 
	 */
	private void validate(ServletWebRequest request) throws ServletRequestBindingException {
		//从session中取ImageCode
		ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.SESSION_KEY);
		
		//从请求中获取输入的imageCode参数
		String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode");
		
		if(StringUtils.isBlank(codeInRequest)) {
			throw new ValidateCodeException("验证码的值不能为空");
		}
	
		if(codeInSession == null) {
			throw new ValidateCodeException("验证码不存在");
		}
		
		//判断验证码是否过期
		if(codeInSession.isExpried()) {
			//如果过期，则将验证码从session中移除
			sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
			throw new ValidateCodeException("验证码已过期");
		}
		
		//匹配session中的验证码和传进来验证码进行比对
		if(!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
			throw new ValidateCodeException("验证码不匹配");
		}
		//成功之后，就把验证码从session中移除
		sessionStrategy.removeAttribute(request, ValidateCodeController.SESSION_KEY);
	}
}
